home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkArgv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  11.2 KB  |  434 lines  |  [TEXT/CWIE]

  1. /*
  2.  * tkArgv.c --
  3.  *
  4.  *    This file contains a procedure that handles table-based
  5.  *    argv-argc parsing.
  6.  *
  7.  * Copyright (c) 1990-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkArgv.c 1.21 97/04/25 16:50:27
  14.  */
  15.  
  16. #include "tkPort.h"
  17. #include "tk.h"
  18.  
  19. /*
  20.  * Default table of argument descriptors.  These are normally available
  21.  * in every application.
  22.  */
  23.  
  24. static Tk_ArgvInfo defaultTable[] = {
  25.     {"-help",    TK_ARGV_HELP,    (char *) NULL,    (char *) NULL,
  26.     "Print summary of command-line options and abort"},
  27.     {NULL,    TK_ARGV_END,    (char *) NULL,    (char *) NULL,
  28.     (char *) NULL}
  29. };
  30.  
  31. /*
  32.  * Forward declarations for procedures defined in this file:
  33.  */
  34.  
  35. static void    PrintUsage _ANSI_ARGS_((Tcl_Interp *interp,
  36.             Tk_ArgvInfo *argTable, int flags));
  37.  
  38. /*
  39.  *----------------------------------------------------------------------
  40.  *
  41.  * Tk_ParseArgv --
  42.  *
  43.  *    Process an argv array according to a table of expected
  44.  *    command-line options.  See the manual page for more details.
  45.  *
  46.  * Results:
  47.  *    The return value is a standard Tcl return value.  If an
  48.  *    error occurs then an error message is left in interp->result.
  49.  *    Under normal conditions, both *argcPtr and *argv are modified
  50.  *    to return the arguments that couldn't be processed here (they
  51.  *    didn't match the option table, or followed an TK_ARGV_REST
  52.  *    argument).
  53.  *
  54.  * Side effects:
  55.  *    Variables may be modified, resources may be entered for tkwin,
  56.  *    or procedures may be called.  It all depends on the arguments
  57.  *    and their entries in argTable.  See the user documentation
  58.  *    for details.
  59.  *
  60.  *----------------------------------------------------------------------
  61.  */
  62.  
  63. int
  64. Tk_ParseArgv(interp, tkwin, argcPtr, argv, argTable, flags)
  65.     Tcl_Interp *interp;        /* Place to store error message. */
  66.     Tk_Window tkwin;        /* Window to use for setting Tk options.
  67.                  * NULL means ignore Tk option specs. */
  68.     int *argcPtr;        /* Number of arguments in argv.  Modified
  69.                  * to hold # args left in argv at end. */
  70.     char **argv;        /* Array of arguments.  Modified to hold
  71.                  * those that couldn't be processed here. */
  72.     Tk_ArgvInfo *argTable;    /* Array of option descriptions */
  73.     int flags;            /* Or'ed combination of various flag bits,
  74.                  * such as TK_ARGV_NO_DEFAULTS. */
  75. {
  76.     register Tk_ArgvInfo *infoPtr;
  77.                 /* Pointer to the current entry in the
  78.                  * table of argument descriptions. */
  79.     Tk_ArgvInfo *matchPtr;    /* Descriptor that matches current argument. */
  80.     char *curArg;        /* Current argument */
  81.     register char c;        /* Second character of current arg (used for
  82.                  * quick check for matching;  use 2nd char.
  83.                  * because first char. will almost always
  84.                  * be '-'). */
  85.     int srcIndex;        /* Location from which to read next argument
  86.                  * from argv. */
  87.     int dstIndex;        /* Index into argv to which next unused
  88.                  * argument should be copied (never greater
  89.                  * than srcIndex). */
  90.     int argc;            /* # arguments in argv still to process. */
  91.     size_t length;        /* Number of characters in current argument. */
  92.     int i;
  93.  
  94.     if (flags & TK_ARGV_DONT_SKIP_FIRST_ARG) {
  95.     srcIndex = dstIndex = 0;
  96.     argc = *argcPtr;
  97.     } else {
  98.     srcIndex = dstIndex = 1;
  99.     argc = *argcPtr-1;
  100.     }
  101.  
  102.     while (argc > 0) {
  103.     curArg = argv[srcIndex];
  104.     srcIndex++;
  105.     argc--;
  106.     length = strlen(curArg);
  107.     if (length > 0) {
  108.         c = curArg[1];
  109.     } else {
  110.         c = 0;
  111.     }
  112.  
  113.     /*
  114.      * Loop throught the argument descriptors searching for one with
  115.      * the matching key string.  If found, leave a pointer to it in
  116.      * matchPtr.
  117.      */
  118.  
  119.     matchPtr = NULL;
  120.     for (i = 0; i < 2; i++) {
  121.         if (i == 0) {
  122.         infoPtr = argTable;
  123.         } else {
  124.         infoPtr = defaultTable;
  125.         }
  126.         for (; (infoPtr != NULL) && (infoPtr->type != TK_ARGV_END);
  127.             infoPtr++) {
  128.          if (infoPtr->key == NULL) {
  129.              continue;
  130.          }
  131.          if ((infoPtr->key[1] != c)
  132.              || (strncmp(infoPtr->key, curArg, length) != 0)) {
  133.              continue;
  134.          }
  135.          if ((tkwin == NULL)
  136.              && ((infoPtr->type == TK_ARGV_CONST_OPTION)
  137.              || (infoPtr->type == TK_ARGV_OPTION_VALUE)
  138.              || (infoPtr->type == TK_ARGV_OPTION_NAME_VALUE))) {
  139.              continue;
  140.          }
  141.          if (infoPtr->key[length] == 0) {
  142.              matchPtr = infoPtr;
  143.              goto gotMatch;
  144.          }
  145.          if (flags & TK_ARGV_NO_ABBREV) {
  146.              continue;
  147.          }
  148.          if (matchPtr != NULL) {
  149.              Tcl_AppendResult(interp, "ambiguous option \"", curArg,
  150.                  "\"", (char *) NULL);
  151.              return TCL_ERROR;
  152.          }
  153.          matchPtr = infoPtr;
  154.         }
  155.     }
  156.     if (matchPtr == NULL) {
  157.  
  158.         /*
  159.          * Unrecognized argument.  Just copy it down, unless the caller
  160.          * prefers an error to be registered.
  161.          */
  162.  
  163.         if (flags & TK_ARGV_NO_LEFTOVERS) {
  164.         Tcl_AppendResult(interp, "unrecognized argument \"",
  165.             curArg, "\"", (char *) NULL);
  166.         return TCL_ERROR;
  167.         }
  168.         argv[dstIndex] = curArg;
  169.         dstIndex++;
  170.         continue;
  171.     }
  172.  
  173.     /*
  174.      * Take the appropriate action based on the option type
  175.      */
  176.  
  177.     gotMatch:
  178.     infoPtr = matchPtr;
  179.     switch (infoPtr->type) {
  180.         case TK_ARGV_CONSTANT:
  181.         *((int *) infoPtr->dst) = (int) infoPtr->src;
  182.         break;
  183.         case TK_ARGV_INT:
  184.         if (argc == 0) {
  185.             goto missingArg;
  186.         } else {
  187.             char *endPtr;
  188.  
  189.             *((int *) infoPtr->dst) =
  190.                 strtol(argv[srcIndex], &endPtr, 0);
  191.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  192.             Tcl_AppendResult(interp, "expected integer argument ",
  193.                 "for \"", infoPtr->key, "\" but got \"",
  194.                 argv[srcIndex], "\"", (char *) NULL);
  195.             return TCL_ERROR;
  196.             }
  197.             srcIndex++;
  198.             argc--;
  199.         }
  200.         break;
  201.         case TK_ARGV_STRING:
  202.         if (argc == 0) {
  203.             goto missingArg;
  204.         } else {
  205.             *((char **)infoPtr->dst) = argv[srcIndex];
  206.             srcIndex++;
  207.             argc--;
  208.         }
  209.         break;
  210.         case TK_ARGV_UID:
  211.         if (argc == 0) {
  212.             goto missingArg;
  213.         } else {
  214.             *((Tk_Uid *)infoPtr->dst) = Tk_GetUid(argv[srcIndex]);
  215.             srcIndex++;
  216.             argc--;
  217.         }
  218.         break;
  219.         case TK_ARGV_REST:
  220.         *((int *) infoPtr->dst) = dstIndex;
  221.         goto argsDone;
  222.         case TK_ARGV_FLOAT:
  223.         if (argc == 0) {
  224.             goto missingArg;
  225.         } else {
  226.             char *endPtr;
  227.  
  228.             *((double *) infoPtr->dst) =
  229.                 strtod(argv[srcIndex], &endPtr);
  230.             if ((endPtr == argv[srcIndex]) || (*endPtr != 0)) {
  231.             Tcl_AppendResult(interp, "expected floating-point ",
  232.                 "argument for \"", infoPtr->key,
  233.                 "\" but got \"", argv[srcIndex], "\"",
  234.                 (char *) NULL);
  235.             return TCL_ERROR;
  236.             }
  237.             srcIndex++;
  238.             argc--;
  239.         }
  240.         break;
  241.         case TK_ARGV_FUNC: {
  242.         typedef int (ArgvFunc)_ANSI_ARGS_((char *, char *, char *));
  243.         ArgvFunc *handlerProc;
  244.  
  245.         handlerProc = (ArgvFunc *) infoPtr->src;
  246.         if ((*handlerProc)(infoPtr->dst, infoPtr->key,
  247.             argv[srcIndex])) {
  248.             srcIndex += 1;
  249.             argc -= 1;
  250.         }
  251.         break;
  252.         }
  253.         case TK_ARGV_GENFUNC: {
  254.         typedef int (ArgvGenFunc)_ANSI_ARGS_((char *, Tcl_Interp *, 
  255.             char *, int, char **));
  256.         ArgvGenFunc *handlerProc;
  257.  
  258.         handlerProc = (ArgvGenFunc *) infoPtr->src;
  259.         argc = (*handlerProc)(infoPtr->dst, interp, infoPtr->key,
  260.             argc, argv+srcIndex);
  261.         if (argc < 0) {
  262.             return TCL_ERROR;
  263.         }
  264.         break;
  265.         }
  266.         case TK_ARGV_HELP:
  267.         PrintUsage (interp, argTable, flags);
  268.         return TCL_ERROR;
  269.         case TK_ARGV_CONST_OPTION:
  270.         Tk_AddOption(tkwin, infoPtr->dst, infoPtr->src,
  271.             TK_INTERACTIVE_PRIO);
  272.         break;
  273.         case TK_ARGV_OPTION_VALUE:
  274.         if (argc < 1) {
  275.             goto missingArg;
  276.         }
  277.         Tk_AddOption(tkwin, infoPtr->dst, argv[srcIndex],
  278.             TK_INTERACTIVE_PRIO);
  279.         srcIndex++;
  280.         argc--;
  281.         break;
  282.         case TK_ARGV_OPTION_NAME_VALUE:
  283.         if (argc < 2) {
  284.             Tcl_AppendResult(interp, "\"", curArg,
  285.                 "\" option requires two following arguments",
  286.                 (char *) NULL);
  287.             return TCL_ERROR;
  288.         }
  289.         Tk_AddOption(tkwin, argv[srcIndex], argv[srcIndex+1],
  290.             TK_INTERACTIVE_PRIO);
  291.         srcIndex += 2;
  292.         argc -= 2;
  293.         break;
  294.         default:
  295.         sprintf(interp->result, "bad argument type %d in Tk_ArgvInfo",
  296.             infoPtr->type);
  297.         return TCL_ERROR;
  298.     }
  299.     }
  300.  
  301.     /*
  302.      * If we broke out of the loop because of an OPT_REST argument,
  303.      * copy the remaining arguments down.
  304.      */
  305.  
  306.     argsDone:
  307.     while (argc) {
  308.     argv[dstIndex] = argv[srcIndex];
  309.     srcIndex++;
  310.     dstIndex++;
  311.     argc--;
  312.     }
  313.     argv[dstIndex] = (char *) NULL;
  314.     *argcPtr = dstIndex;
  315.     return TCL_OK;
  316.  
  317.     missingArg:
  318.     Tcl_AppendResult(interp, "\"", curArg,
  319.         "\" option requires an additional argument", (char *) NULL);
  320.     return TCL_ERROR;
  321. }
  322.  
  323. /*
  324.  *----------------------------------------------------------------------
  325.  *
  326.  * PrintUsage --
  327.  *
  328.  *    Generate a help string describing command-line options.
  329.  *
  330.  * Results:
  331.  *    Interp->result will be modified to hold a help string
  332.  *    describing all the options in argTable, plus all those
  333.  *    in the default table unless TK_ARGV_NO_DEFAULTS is
  334.  *    specified in flags.
  335.  *
  336.  * Side effects:
  337.  *    None.
  338.  *
  339.  *----------------------------------------------------------------------
  340.  */
  341.  
  342. static void
  343. PrintUsage(interp, argTable, flags)
  344.     Tcl_Interp *interp;        /* Place information in this interp's
  345.                  * result area. */
  346.     Tk_ArgvInfo *argTable;    /* Array of command-specific argument
  347.                  * descriptions. */
  348.     int flags;            /* If the TK_ARGV_NO_DEFAULTS bit is set
  349.                  * in this word, then don't generate
  350.                  * information for default options. */
  351. {
  352.     register Tk_ArgvInfo *infoPtr;
  353.     int width, i, numSpaces;
  354. #define NUM_SPACES 20
  355.     static char spaces[] = "                    ";
  356.     char tmp[30];
  357.  
  358.     /*
  359.      * First, compute the width of the widest option key, so that we
  360.      * can make everything line up.
  361.      */
  362.  
  363.     width = 4;
  364.     for (i = 0; i < 2; i++) {
  365.     for (infoPtr = i ? defaultTable : argTable;
  366.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  367.         int length;
  368.         if (infoPtr->key == NULL) {
  369.         continue;
  370.         }
  371.         length = strlen(infoPtr->key);
  372.         if (length > width) {
  373.         width = length;
  374.         }
  375.     }
  376.     }
  377.  
  378.     Tcl_AppendResult(interp, "Command-specific options:", (char *) NULL);
  379.     for (i = 0; ; i++) {
  380.     for (infoPtr = i ? defaultTable : argTable;
  381.         infoPtr->type != TK_ARGV_END; infoPtr++) {
  382.         if ((infoPtr->type == TK_ARGV_HELP) && (infoPtr->key == NULL)) {
  383.         Tcl_AppendResult(interp, "\n", infoPtr->help, (char *) NULL);
  384.         continue;
  385.         }
  386.         Tcl_AppendResult(interp, "\n ", infoPtr->key, ":", (char *) NULL);
  387.         numSpaces = width + 1 - strlen(infoPtr->key);
  388.         while (numSpaces > 0) {
  389.         if (numSpaces >= NUM_SPACES) {
  390.             Tcl_AppendResult(interp, spaces, (char *) NULL);
  391.         } else {
  392.             Tcl_AppendResult(interp, spaces+NUM_SPACES-numSpaces,
  393.                 (char *) NULL);
  394.         }
  395.         numSpaces -= NUM_SPACES;
  396.         }
  397.         Tcl_AppendResult(interp, infoPtr->help, (char *) NULL);
  398.         switch (infoPtr->type) {
  399.         case TK_ARGV_INT: {
  400.             sprintf(tmp, "%d", *((int *) infoPtr->dst));
  401.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  402.                 tmp, (char *) NULL);
  403.             break;
  404.         }
  405.         case TK_ARGV_FLOAT: {
  406.             sprintf(tmp, "%g", *((double *) infoPtr->dst));
  407.             Tcl_AppendResult(interp, "\n\t\tDefault value: ",
  408.                 tmp, (char *) NULL);
  409.             break;
  410.         }
  411.         case TK_ARGV_STRING: {
  412.             char *string;
  413.  
  414.             string = *((char **) infoPtr->dst);
  415.             if (string != NULL) {
  416.             Tcl_AppendResult(interp, "\n\t\tDefault value: \"",
  417.                 string, "\"", (char *) NULL);
  418.             }
  419.             break;
  420.         }
  421.         default: {
  422.             break;
  423.         }
  424.         }
  425.     }
  426.  
  427.     if ((flags & TK_ARGV_NO_DEFAULTS) || (i > 0)) {
  428.         break;
  429.     }
  430.     Tcl_AppendResult(interp, "\nGeneric options for all commands:",
  431.         (char *) NULL);
  432.     }
  433. }
  434.